package io.github.uicdb.et.block.multipart;

import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.LongArrayTag;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.pattern.BlockPattern;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public abstract class MultiPartControllerBE extends BlockEntity {
    protected List<BlockPos> parts = new ArrayList<>();

    public MultiPartControllerBE(BlockEntityType<?> type, BlockPos pos, BlockState blockState) {
        super(type, pos, blockState);
    }

    @NotNull
    protected abstract BlockPattern getPattern();

    private BlockPattern patternCache;
    protected BlockPattern getPatternWithCache() {
        if (patternCache == null) {
            patternCache = getPattern();
        }
        return patternCache;
    }
    protected void checkOrReloadMultiBlock() {
        if (level == null || level.isClientSide) return;
        BlockPattern pattern = getPatternWithCache();
        var find = pattern.find(level, this.getBlockPos());
        if (find == null) {
            level.setBlock(this.getBlockPos(), this.getBlockState().setValue(MultiPartControllerBlock.FORMED, Boolean.FALSE), 3);
            parts.forEach(pos -> {
                unformed(pos, level.getBlockState(pos));
            });
        } else {
            level.setBlock(this.getBlockPos(), this.getBlockState().setValue(MultiPartControllerBlock.FORMED, Boolean.TRUE), 3);
            parts.forEach(pos -> {
                formed(pos, level.getBlockState(pos));
            });
            parts.clear();
            for (int i = 0; i < pattern.getWidth(); i++) {
                for (int j = 0; j < pattern.getHeight(); j++) {
                    for (int k = 0; k < pattern.getDepth(); k++) {
                        var blockinworld = find.getBlock(i, j, k);
                        var findPos = blockinworld.getPos();
                        if (blockinworld.getState().hasProperty(MultiPartControllerBlock.FORMED)) {
                            parts.add(findPos);
                            formed(findPos, level.getBlockState(findPos));
                        }
                    }
                }
            }
        }
    }

    protected void unformed(BlockPos pos, BlockState state) {
        if (state.hasProperty(MultiPartControllerBlock.FORMED)) {
            state.setValue(MultiPartControllerBlock.FORMED, Boolean.FALSE);
        }
        if (level.getBlockEntity(pos) instanceof MultiPartBE multiPartBE) {
            multiPartBE.setController(Optional.empty());
        }
    }
    protected void unformed() {
        parts.forEach(pos -> {
            unformed(pos, level.getBlockState(pos));
        });
    }

    protected void formed(BlockPos pos, BlockState state) {
        if (state.hasProperty(MultiPartControllerBlock.FORMED)) {
            state.setValue(MultiPartControllerBlock.FORMED, Boolean.TRUE);
        }
        if (level.getBlockEntity(pos) instanceof MultiPartBE multiPartBE) {
            multiPartBE.setController(Optional.of(this));
        }
    }

    @Override
    protected void saveAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.saveAdditional(tag, registries);
        LongArrayTag toSave = new LongArrayTag(parts.stream().map(BlockPos::asLong).toList());
        tag.put("parts", toSave);
    }

    @Override
    protected void loadAdditional(@NotNull CompoundTag tag, HolderLookup.@NotNull Provider registries) {
        super.loadAdditional(tag, registries);
        parts.clear();
        parts.addAll(Arrays.stream(tag.getLongArray("parts")).mapToObj(BlockPos::of).toList());
        checkOrReloadMultiBlock();
    }
}
